﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Sockets;
using System.Text;

/// <summary>
/// Summary description for Message
/// </summary>
public class Message
{
    //<##                   [INT 2 BYTES]>
    //<MESSAGELENGTH        [INT 4 BYTES]>
    //<COMMAND [HEXA CODE]  [USHORT 2 BYTES]>
    //<WPARAM               [INT 8 BYTES]>
    //<LPARAM               [INT 4 BYTES]>
    //<DATA>                [ALWAYS BEGIN WITH 1 BYTE CHECK ZIP DATA]
    //<\R\N                 [INT 2 BYTES]>

    private static readonly int HEADER_LENGTH = 20;
    private static readonly int COMPRESSION_OPTION_LENGTH = 1;
    public static readonly int EMPTY_MESSAGE_LENGTH = 22;

    private int m_nMessageLength;               //4 bytes
    private ushort m_uCommand;                     //2 bytes
    private Int64 m_nWPARAM;//usually DeviceID    //8 bytes
    private int m_nLPARAM;//usually UserID      //4 bytes

    private byte m_Compression;
    private byte[] m_pDataRef;
    private int m_DataOffset;
    private int m_DataSize;

    public Message()
    {
        m_nMessageLength = 0;
        m_uCommand = 0;
        m_nWPARAM = 0;
        m_nLPARAM = 0;

        m_pDataRef = null;
        m_DataOffset = 0;
        m_DataSize = 0;

        m_nMessageLength = EMPTY_MESSAGE_LENGTH;
    }

    public Message(ushort uCommand, Int64 nWPARAM, int nLPARAM)
    {
        m_uCommand = uCommand;
        m_nWPARAM = nWPARAM;
        m_nLPARAM = nLPARAM;

        m_pDataRef = null;
        m_DataOffset = 0;
        m_DataSize = 0;

        m_nMessageLength = EMPTY_MESSAGE_LENGTH;
    }

    public Message(ushort uCommand, Int64 nWPARAM, int nLPARAM, byte[] srcData, int nSrcDataSize)
    {
        m_uCommand = uCommand;
        m_nWPARAM = nWPARAM;
        m_nLPARAM = nLPARAM;

        m_pDataRef = srcData;
        m_DataOffset = 0;
        m_DataSize = nSrcDataSize;

        m_nMessageLength = EMPTY_MESSAGE_LENGTH + COMPRESSION_OPTION_LENGTH + m_DataSize;
    }

    public Message(ushort uCommand, Int64 nWPARAM, int nLPARAM, byte[] srcData, int nSrcOffset, int nSrcDataSize)
    {
        m_uCommand = uCommand;
        m_nWPARAM = nWPARAM;
        m_nLPARAM = nLPARAM;

        m_pDataRef = srcData;
        m_DataOffset = nSrcOffset;
        m_DataSize = nSrcDataSize;

        m_nMessageLength = EMPTY_MESSAGE_LENGTH + COMPRESSION_OPTION_LENGTH + m_DataSize;
    }

    public int MessageLength
    {
        get { return m_nMessageLength; }
        set { m_nMessageLength = value; }
    }

    public ushort Command
    {
        get { return m_uCommand; }
        set { m_uCommand = value; }
    }

    public Int64 WPARAM
    {
        get { return m_nWPARAM; }
        set { m_nWPARAM = value; }
    }

    public int LPARAM
    {
        get { return m_nLPARAM; }
        set { m_nLPARAM = value; }
    }

    public byte[] Data
    {
        get { return m_pDataRef; }
    }

    public int DataOffset
    {
        get { return m_DataOffset; }
    }

    public int DataSize
    {
        get { return m_DataSize; }
    }

    public void SetData(byte[] srcData, int nSrcOffset, int nSrcDataSize)
    {
        m_pDataRef = srcData;
        m_DataOffset = nSrcOffset;
        m_DataSize = nSrcDataSize;

        m_nMessageLength = EMPTY_MESSAGE_LENGTH + COMPRESSION_OPTION_LENGTH + m_DataSize;
    }

    public void SetData(byte[] srcData)
    {
        m_pDataRef = srcData;
        m_DataOffset = 0;
        m_DataSize = srcData.Length;

        m_nMessageLength = EMPTY_MESSAGE_LENGTH + COMPRESSION_OPTION_LENGTH + m_DataSize;
    }

    public void Format(ushort uCommand, Int64 nWPARAM, int nLPARAM, byte[] srcData, int nSrcDataSize)
    {
        m_uCommand = uCommand;
        m_nWPARAM = nWPARAM;
        m_nLPARAM = nLPARAM;

        m_pDataRef = srcData;
        m_DataOffset = 0;
        m_DataSize = nSrcDataSize;

        m_nMessageLength = EMPTY_MESSAGE_LENGTH + COMPRESSION_OPTION_LENGTH + m_DataSize;
    }

    public void Format(ushort uCommand, Int64 nWPARAM, int nLPARAM, byte[] srcData, int nSrcOffset, int nSrcDataSize)
    {
        m_uCommand = uCommand;
        m_nWPARAM = nWPARAM;
        m_nLPARAM = nLPARAM;

        m_pDataRef = srcData;
        m_DataOffset = nSrcOffset;
        m_DataSize = nSrcDataSize;

        m_nMessageLength = EMPTY_MESSAGE_LENGTH + COMPRESSION_OPTION_LENGTH + m_DataSize;
    }

    public bool Parse(byte[] srcData, int nSrcSize)
    {
        try
        {
            m_nMessageLength = BitConverter.ToInt32(srcData, 2);
            m_uCommand = BitConverter.ToUInt16(srcData, 6);
            m_nWPARAM = BitConverter.ToInt64(srcData, 8);
            m_nLPARAM = BitConverter.ToInt32(srcData, 16);

            if (m_nMessageLength > EMPTY_MESSAGE_LENGTH)
            {
                m_pDataRef = srcData;
                m_DataOffset = HEADER_LENGTH + COMPRESSION_OPTION_LENGTH;
                m_DataSize = m_nMessageLength - EMPTY_MESSAGE_LENGTH - COMPRESSION_OPTION_LENGTH;
            }
            else
            {
                m_pDataRef = srcData;
                m_DataOffset = HEADER_LENGTH;
                m_DataSize = m_nMessageLength - EMPTY_MESSAGE_LENGTH;
            }
        }
        catch
        {
            m_uCommand = 0;
            m_nWPARAM = 0;
            m_nLPARAM = 0;

            m_pDataRef = null;
            m_DataOffset = 0;
            m_DataSize = 0;

            m_nMessageLength = EMPTY_MESSAGE_LENGTH;
            return false;
        }

        return true;
    }

    public bool Parse(byte[] srcData, int nSrcOffset, int nSrcSize)
    {
        try
        {
            m_nMessageLength = BitConverter.ToInt32(srcData, nSrcOffset + 2);
            m_uCommand = BitConverter.ToUInt16(srcData, nSrcOffset + 6);
            m_nWPARAM = BitConverter.ToInt64(srcData, nSrcOffset + 8);
            m_nLPARAM = BitConverter.ToInt32(srcData, nSrcOffset + 16);

            if (m_nMessageLength > EMPTY_MESSAGE_LENGTH)         
            {
                m_pDataRef = srcData;
                m_DataOffset = nSrcOffset + HEADER_LENGTH + COMPRESSION_OPTION_LENGTH;
                m_DataSize = m_nMessageLength - EMPTY_MESSAGE_LENGTH - COMPRESSION_OPTION_LENGTH;
                m_Compression = m_pDataRef[m_DataOffset - 1];
            }
            else
            {
                m_pDataRef = srcData;
                m_DataOffset = nSrcOffset + HEADER_LENGTH;
                m_DataSize = m_nMessageLength - EMPTY_MESSAGE_LENGTH;
            }
        }
        catch
        {
            m_uCommand = 0;
            m_nWPARAM = 0;
            m_nLPARAM = 0;

            m_pDataRef = null;
            m_DataOffset = 0;
            m_DataSize = 0;

            m_nMessageLength = EMPTY_MESSAGE_LENGTH;
            return false;
        }

        return true;
    }

    public string GetDataAsString()
    {
        return Encoding.Unicode.GetString(m_pDataRef, m_DataOffset, m_DataSize);
    }

    public int Send(Socket sock, byte[] msgMarker)
    {
        int nSent = 0;

        try
        {
            //Send 2 byte header mark
            nSent += sock.Send(msgMarker, 2, SocketFlags.None);

            nSent += sock.Send(BitConverter.GetBytes(m_nMessageLength));
            nSent += sock.Send(BitConverter.GetBytes(m_uCommand));
            nSent += sock.Send(BitConverter.GetBytes(m_nWPARAM));
            nSent += sock.Send(BitConverter.GetBytes(m_nLPARAM));

            //Send contents
            if (m_DataSize > 0)
            {
                nSent += sock.Send(new byte[] { m_Compression }, 0, 1, SocketFlags.None);
                nSent += sock.Send(m_pDataRef, m_DataOffset, m_DataSize, SocketFlags.None);
            }

            //Send 2 byte footer mark
            nSent += sock.Send(msgMarker, 2, 2, SocketFlags.None);
        }
        catch (Exception e)
        {
            nSent = -1;
        }

        return nSent;
    }
}